﻿/*
Digraph (library for building, traversal, comparison and saving of directed
graph).

Copyright (C) 2014 Dmitry Merzagitovich Arslanov

This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*/

using System;
using System.Collections.Generic;
using System.Linq;

namespace Digraph
{
    /// <summary>
    /// List of connected digraph arcs.
    /// </summary>
    public sealed class Path<TVertex, TArc> : List<Arc<TVertex, TArc>>
    {
        /// <summary>
        /// Create new empty instance of path.
        /// </summary>
        public Path()
        {
        }

        /// <summary>
        /// Create new instance of path and allocate space for specified number
        /// of arcs.
        /// </summary>
        /// <param name="capacity">Number of arcs in path.</param>
        public Path(int capacity)
            : base(capacity)
        {
        }

        /// <summary>
        /// Create new instance of path and initialize it by arcs from
        /// collection.
        /// </summary>
        /// <param name="collection">Arc collection for initialization.</param>
        public Path(IEnumerable<Arc<TVertex, TArc>> collection)
            : base(collection)
        {
        }

        /// <summary>
        /// Calculate length of path.
        /// </summary>
        /// <param name="arcLengthCalculator">
        /// User supplied delegate for calculation of digraph arc length.
        /// </param>
        /// <returns>Length of path.</returns>
        /// <exception cref="ArgumentNullException">
        /// This exception is thrown if arcLengthCalculator is null.
        /// </exception>
        public double CalcLength(
            ArcLengthCalculator<TVertex, TArc> arcLengthCalculator)
        {
            if (arcLengthCalculator == null) throw new ArgumentNullException();
            double length = 0.0;
            this.ForEach(arc => length += arcLengthCalculator(arc));
            return length;
        }

        /// <summary>
        /// Get minimum sub path between two vertices.
        /// </summary>
        /// <param name="fromVertex">Begin vertex of sub path.</param>
        /// <param name="toVertex">End vertex of sub path.</param>
        /// <returns>
        /// Minimum sub path between two vertices or null if there is no such
        /// vertex at path.
        /// </returns>
        public Path<TVertex, TArc> GetSubPath(Vertex<TVertex, TArc> fromVertex,
                                              Vertex<TVertex, TArc> toVertex)
        {
            int startIndex = FindIndex(
                        arc => arc.FromVertex == fromVertex);
            if (startIndex < 0) return null;
            int stopIndex = FindIndex(
                arc => arc.ToVertex == toVertex);
            if (stopIndex < 0) return null;
            List<Arc<TVertex, TArc>> range = GetRange(startIndex,
                stopIndex - startIndex + 1);
            return new Path<TVertex, TArc>(range);
        }
    }
}
